#pragma once

#include "oj_model.hpp"
#include "oj_view.hpp"
#include "../comm/log.hpp"
#include "../comm/util.hpp"
#include "../comm/httplib.h"

#include <mutex>
#include <vector>
#include <string>
#include <cassert>
#include <fstream>

#include <jsoncpp/json/json.h>

#include "../../api/cpp/ml_api/ml_api.h"

namespace ns_control
{
    using namespace ns_model;
    using namespace ns_view;
    using namespace ns_log;
    using namespace ns_util;
    using namespace httplib;

    class Control
    {
    private:
        Model _model;
        View _view;
        ml_client api;
        UserModel user_model;
        int modid;
        int cmdid;

        bool LoadConfig()
        {
            std::ifstream config_file("./conf/oj.conf");
            if (!config_file.is_open())
            {
                LOG(ERROR) << "无法打开配置文件" << endl;
                return false;
            }

            std::string line;
            while (std::getline(config_file, line))
            {
                if (line.empty() || line[0] == '#' || line[0] == '[')
                    continue;

                size_t pos = line.find('=');
                if (pos == std::string::npos)
                    continue;

                std::string key = line.substr(0, pos);
                std::string value = line.substr(pos + 1);
                
                // 去除首尾空格
                key.erase(0, key.find_first_not_of(" \t"));
                key.erase(key.find_last_not_of(" \t") + 1);
                value.erase(0, value.find_first_not_of(" \t"));
                value.erase(value.find_last_not_of(" \t") + 1);

                if (key == "modid")
                    modid = std::stoi(value);
                else if (key == "cmdid")
                    cmdid = std::stoi(value);
            }

            return true;
        }

    public:
        Control() : modid(1), cmdid(1)
        {
            if (!LoadConfig())
            {
                LOG(ERROR) << "加载配置失败，使用默认值 modid=1, cmdid=1" << endl;
            }
            else
            {
                LOG(INFO) << "成功加载配置：modid=" << modid << ", cmdid=" << cmdid << endl;
            }
        }
        ~Control() {}

    public:
        void Login(const Request &req, Response &res)
        {
            string body = req.body;
            Json::Value login_info;
            Json::Reader().parse(body, login_info);

            string username = login_info["username"].asString();
            string password = login_info["password"].asString();

            LOG(INFO) << "用户尝试登录: " << username << endl;

            if (user_model.Login(username, password))
            {
                // 设置Session Cookie，去除 HttpOnly 以便 JavaScript 能读取
                string cookie = "user_id=" + username + "; Path=/; SameSite=Lax; Max-Age=86400";
                res.set_header("Set-Cookie", cookie);
                res.set_header("Cache-Control", "no-store, no-cache, must-revalidate");
                res.set_header("Pragma", "no-cache");
                res.set_header("Expires", "0");
                res.set_content("登录成功", "text/plain");
                LOG(INFO) << "用户登录成功: " << username << ", Cookie: " << cookie << endl;
            }
            else
            {
                res.status = 403;
                res.set_content("用户名或密码错误", "text/plain");
                LOG(ERROR) << "用户登录失败: " << username << endl;
            }
        }

        void Register(const Request &req, Response &res)
        {
            Json::Value reg_info;
            Json::Reader().parse(req.body, reg_info);

            string username = reg_info["username"].asString();
            string password = reg_info["password"].asString();

            if (user_model.Register(username, password))
            {
                res.set_content("注册成功", "text/plain");
            }
            else
            {
                res.status = 409;
                res.set_content("用户名已存在", "text/plain");
            }
        }

        // 新增页面渲染方法
        void LoginPage(std::string *html)
        {
            const std::string path = "./template_html/login.html";
            ctemplate::Template *tpl = ctemplate::Template::GetTemplate(path, ctemplate::DO_NOT_STRIP);
            tpl->Expand(html, nullptr);
        }

        void RegisterPage(std::string *html)
        {
            const std::string path = "./template_html/register.html";
            ctemplate::Template *tpl = ctemplate::Template::GetTemplate(path, ctemplate::DO_NOT_STRIP);
            tpl->Expand(html, nullptr);
        }
        void Logout(const Request &req, Response &res)
        {
            // 清除名为 "user_id" 的 Cookie，确保路径正确，去除 HttpOnly
            string cookie = "user_id=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax";
            res.set_header("Set-Cookie", cookie);
            LOG(INFO) << "用户退出，清除Cookie: " << cookie << endl;
            res.set_redirect("/");
        }
        bool AllQuestions(string *html)
        {
            vector<Question> all;
            if (_model.GetAllQuestions(&all))
            {
                // 把所以的题目进行渲染
                _view.AllExpand(all, html);

                return true;
            }
            else
            {
                *html = "渲染全部题目失败";
                LOG(ERROR) << "渲染全部题目失败" << endl;
                // 失败
                return false;
            }
        }

        bool OneQuestion(const string &number, string *html)
        {
            Question q;
            if (_model.GetOneQuestion(number, &q))
            {
                // 成功，渲染
                _view.OneExpand(q, html);

                return true;
            }
            else
            {
                *html = "渲染一个题目失败";
                LOG(ERROR) << "渲染一个题目失败" << endl;
                // 失败
                return false;
            }
        }

        void Judge(const string &number, const Request &req, string *out_json)
        {
            const string &in_json = req.body;
            Question q;
            _model.GetOneQuestion(number, &q);
            Json::Reader reader;
            Json::Value in_value;
            reader.parse(in_json, in_value);
            string code = in_value["code"].asString();
            string language = in_value["language"].asString();

            // 获取用户身份
            string user_id;
            GetUserIdFromCookie(req, user_id);

            Json::Value compile_value;
            compile_value["input"] = in_value["input"].asString();
            compile_value["code"] = code;
            compile_value["language"] = language;
            compile_value["test_input"] = q.test_input;
            compile_value["test_output"] = q.test_output;
            compile_value["cpu"] = q.cpu_limit;
            compile_value["mem"] = q.mem_limit;

            Json::StyledWriter writer;
            string compile_string = writer.write(compile_value);

            Json::Value result;
            result["status"] = "运行中"; // 初始状态
            result["time"] = "-";
            result["memory"] = "-";
            result["output"] = "";
            result["error"] = "";

            if (api.reg_init(modid, cmdid) != 0)
            {
                std::cout << "modid " << modid << ", cmdid " << cmdid << " still not exist host, after register"  << std::endl;
            }
            
            int retry_count = 0;
            const int MAX_RETRIES = 3;
            
            while (retry_count < MAX_RETRIES)
            {
                string ip;
                int port;
                int ret = api.get_host(modid, cmdid, ip, port);
                if (ret == 0)
                {
                    Client cli(ip, port);
                    // 设置连接超时和读取超时时间为1秒
                    cli.set_connection_timeout(30, 0);
                    cli.set_read_timeout(30, 0);
                    
                    // 先尝试建立连接，验证服务器是否存活
                    if (!cli.is_valid())
                    {
                        LOG(INFO) << "服务器不可用：" << ip << ":" << port << endl;
                        api.report(modid, cmdid, ip, port, 1);
                        retry_count++;
                        continue;  // 继续尝试获取新的主机
                    }

                    if (auto ret_post = cli.Post("/compile_run", compile_string, "application/json;charset=utf-8"))
                    {
                        if (ret_post->status == 200)
                        {
                            api.report(modid, cmdid, ip, port, 0);

                            Json::Value response;
                            if (!Json::Reader().parse(ret_post->body, response))
                            {
                                LOG(ERROR) << "解析响应JSON失败" << endl;
                                result["result_code"] = -2; // 内部错误
                                break;
                            }

                            // 更新状态
                            if (response.isMember("result_code")) {
                                result["result_code"] = response["result_code"].asInt();
                            } else {
                                result["result_code"] = -2; // 内部错误
                            }

                            // 根据result_code设置状态
                            int result_code = result["result_code"].asInt();
                            if (result_code == 0) {
                                result["status"] = result["pass_rate"].asFloat() == 100.0f ? "通过" : "未通过";
                            } else if (result_code == 1) {
                                result["status"] = "未通过";
                            } else if (result_code == -1) {
                                result["status"] = "空代码";
                            } else if (result_code == -2) {
                                result["status"] = "内部错误";
                            } else if (result_code == -3) {
                                result["status"] = "编译错误";
                            } else if (result_code == 6 || result_code == 134) { // SIGABRT
                                result["status"] = "内存超出";
                            } else if (result_code == 24 || result_code == 152) { // SIGXCPU
                                result["status"] = "运行超时";
                            } else if (result_code == 8 || result_code == 136) { // SIGFPE
                                result["status"] = "浮点错误";
                            } else if (result_code == 11 || result_code == 139) { // SIGSEGV
                                result["status"] = "段错误";
                            } else {
                                result["status"] = "运行错误";
                            }

                            // 更新其他字段
                            if (response.isMember("output")) {
                                result["output"] = response["output"].asString();
                            }
                            if (response.isMember("pass_rate")) {
                                result["pass_rate"] = response["pass_rate"].asFloat();
                            }
                            if (response.isMember("error_case")) {
                                result["error_case"] = response["error_case"].asString();
                            }
                            if (response.isMember("error")) {
                                result["error"] = response["error"].asString();
                            }
                            if (response.isMember("time")) {
                                result["time"] = response["time"].asString();
                            }
                            if (response.isMember("memory")) {
                                result["memory"] = response["memory"].asString();
                            }

                            // 记录提交
                            _model.AddSubmission(user_id, number, result["status"].asString());

                            *out_json = writer.write(result);
                            break;
                        }
                        else
                        {
                            LOG(ERROR) << "请求编译运行服务失败" << endl;
                            result["result_code"] = -2; // 内部错误
                            api.report(modid, cmdid, ip, port, 1);
                            retry_count++;
                        }
                    }
                    else
                    {
                        LOG(INFO) << "主机下线："<< "主机详情" << ip << ":" << port << endl;
                        result["result_code"] = -2; // 内部错误
                        api.report(modid, cmdid, ip, port, 1);
                        retry_count++;
                    }
                }
                else if (ret == 1)
                {
                    api.report(modid, cmdid, ip, port, 1);
                    retry_count++;
                }
                else if (ret == 2)
                {
                    LOG(ERROR) << "system err" << endl;
                    retry_count++;
                }
                else if (ret == 3)
                {
                    LOG(ERROR) << "not exist" << endl;
                    retry_count++;
                }
                else
                {
                    LOG(ERROR) << "未定义错误" << endl;
                    retry_count++;
                }
            }

            // 如果重试次数达到上限，设置错误信息并返回
            if (retry_count >= MAX_RETRIES)
            {
                result["result_code"] = -2;
                result["status"] = "服务暂时不可用";
                result["error"] = "编译运行服务暂时不可用，请稍后重试";
                *out_json = writer.write(result);
                return;
            }
        }
        void SearchQuestions(const string &keyword, string *html)
        {
            vector<Question> results;
            if (_model.GetQuestionsByTitle(keyword, &results))
            {
                _view.AllExpand(results, html);
            }
            else
            {
                *html = "搜索服务暂不可用";
                LOG(ERROR) << "数据库查询失败" << endl;
            }
        }

        void UserSubmissions(const Request &req, Response &res)
        {
            // 验证登录状态
            string user_id;
            if (!GetUserIdFromCookie(req, user_id))
            {
                res.set_redirect("/login");
                return;
            }

            MYSQL *my = Connect();
            if (my == nullptr)
            {
                LOG(ERROR) << "连接数据库失败" << endl;
                res.status = 500;
                res.set_content("服务器内部错误", "text/plain");
                return;
            }

            // 防SQL注入
            char escaped_user_id[2048] = {0};
            mysql_real_escape_string(my, escaped_user_id, user_id.c_str(), user_id.length());

            string sql = "SELECT q.number, q.title, q.star, s.status, s.submit_time "
                         "FROM oj_submissions s "
                         "JOIN oj_questions q ON s.question_id = q.number "
                         "WHERE s.user_id = '";
            sql += escaped_user_id;
            sql += "' ORDER BY s.submit_time DESC";

            if (mysql_query(my, sql.c_str()) != 0)
            {
                LOG(ERROR) << "查询提交记录失败: " << mysql_error(my) << endl;
                mysql_close(my);
                res.status = 500;
                res.set_content("服务器内部错误", "text/plain");
                return;
            }

            MYSQL_RES *result = mysql_store_result(my);
            if (result == nullptr)
            {
                mysql_close(my);
                res.status = 500;
                res.set_content("服务器内部错误", "text/plain");
                return;
            }

            vector<UserModel::Submission> submissions;
            MYSQL_ROW row;
            while ((row = mysql_fetch_row(result)))
            {
                UserModel::Submission sub;
                sub.number = row[0] ? row[0] : "";
                sub.title = row[1] ? row[1] : "";
                sub.star = row[2] ? row[2] : "";
                sub.status = row[3] ? row[3] : "";
                sub.submit_time = row[4] ? row[4] : "";
                submissions.push_back(sub);
            }

            mysql_free_result(result);
            mysql_close(my);

            // 渲染页面
            string html;
            RenderSubmissions(submissions, &html);
            res.set_content(html, "text/html");
        }

        void AdminLogin(const Request &req, Response &res)
        {
            string body = req.body;
            Json::Value login_info;
            Json::Reader().parse(body, login_info);

            string username = login_info["username"].asString();
            string password = login_info["password"].asString();

            if (_model.IsAdmin(username, password))
            {
                res.set_header("Set-Cookie", "admin=" + username + "; Path=/; Max-Age=3600");
                Json::Value response;
                response["status"] = 0;
                response["message"] = "登录成功";
                res.set_content(Json::FastWriter().write(response), "application/json");
            }
            else
            {
                Json::Value response;
                response["status"] = 1;
                response["message"] = "用户名或密码错误";
                res.set_content(Json::FastWriter().write(response), "application/json");
            }
        }

        void AdminLoginPage(string *html)
        {
            const string path = "./template_html/admin_login.html";
            ctemplate::Template *tpl = ctemplate::Template::GetTemplate(path, ctemplate::DO_NOT_STRIP);
            tpl->Expand(html, nullptr);
        }

        void AdminPage(string *html)
        {
            const string path = "./template_html/admin.html";
            ctemplate::Template *tpl = ctemplate::Template::GetTemplate(path, ctemplate::DO_NOT_STRIP);
            tpl->Expand(html, nullptr);
        }

        void AddQuestion(const Request &req, Response &res)
        {
            string admin;
            if (!GetAdminFromCookie(req, admin))
            {
                Json::Value response;
                response["status"] = 1;
                response["message"] = "请先登录";
                res.set_content(Json::FastWriter().write(response), "application/json");
                return;
            }

            string body = req.body;
            Json::Value question_info;
            Json::Reader().parse(body, question_info);

            Question q;
            q.number = question_info["number"].asString();
            q.title = question_info["title"].asString();
            q.star = question_info["star"].asString();
            q.desc = question_info["desc"].asString();
            q.head = question_info["head"].asString();
            q.test_input = question_info["test_input"].asString();
            q.test_output = question_info["test_output"].asString();
            q.cpu_limit = question_info["cpu_limit"].asInt();
            q.mem_limit = question_info["mem_limit"].asInt();

            if (_model.AddQuestion(q))
            {
                Json::Value response;
                response["status"] = 0;
                response["message"] = "添加题目成功";
                res.set_content(Json::FastWriter().write(response), "application/json");
            }
            else
            {
                Json::Value response;
                response["status"] = 1;
                response["message"] = "添加题目失败";
                res.set_content(Json::FastWriter().write(response), "application/json");
            }
        }

        void UpdateQuestion(const Request &req, Response &res)
        {
            string admin;
            if (!GetAdminFromCookie(req, admin))
            {
                Json::Value response;
                response["status"] = 1;
                response["message"] = "请先登录";
                res.set_content(Json::FastWriter().write(response), "application/json");
                return;
            }

            string body = req.body;
            Json::Value question_info;
            Json::Reader().parse(body, question_info);

            Question q;
            q.number = question_info["number"].asString();
            q.title = question_info["title"].asString();
            q.star = question_info["star"].asString();
            q.desc = question_info["desc"].asString();
            q.head = question_info["head"].asString();
            q.test_input = question_info["test_input"].asString();
            q.test_output = question_info["test_output"].asString();
            q.cpu_limit = question_info["cpu_limit"].asInt();
            q.mem_limit = question_info["mem_limit"].asInt();

            if (_model.UpdateQuestion(q))
            {
                Json::Value response;
                response["status"] = 0;
                response["message"] = "更新题目成功";
                res.set_content(Json::FastWriter().write(response), "application/json");
            }
            else
            {
                Json::Value response;
                response["status"] = 1;
                response["message"] = "更新题目失败";
                res.set_content(Json::FastWriter().write(response), "application/json");
            }
        }

        void DeleteQuestion(const Request &req, Response &res)
        {
            string admin;
            if (!GetAdminFromCookie(req, admin))
            {
                LOG(ERROR) << "管理员未登录，删除题目失败" << endl;
                Json::Value response;
                response["status"] = 1;
                response["message"] = "请先登录";
                res.set_content(Json::FastWriter().write(response), "application/json");
                return;
            }
            LOG(INFO) << "管理员 " << admin << " 尝试删除题目" << endl;

            string number = req.matches[1].str();
            LOG(INFO) << "准备删除题目编号: " << number << endl;

            if (_model.DeleteQuestion(number))
            {
                LOG(INFO) << "题目 " << number << " 删除成功" << endl;
                Json::Value response;
                response["status"] = 0;
                response["message"] = "删除题目成功";
                res.set_content(Json::FastWriter().write(response), "application/json");
            }
            else
            {
                LOG(ERROR) << "题目 " << number << " 删除失败" << endl;
                Json::Value response;
                response["status"] = 1;
                response["message"] = "删除题目失败";
                res.set_content(Json::FastWriter().write(response), "application/json");
            }
        }

        // 论坛相关功能
        void ForumPage(const string &question_number, string *html)
        {
            vector<ForumPost> posts;
            if (_model.GetForumPosts(question_number, &posts))
            {
                ctemplate::TemplateDictionary dict("forum");
                dict.SetValue("question_number", question_number);

                for (const auto &post : posts)
                {
                    ctemplate::TemplateDictionary *sub_dict = dict.AddSectionDictionary("posts");
                    sub_dict->SetIntValue("id", post.id);
                    sub_dict->SetValue("title", post.title);
                    sub_dict->SetValue("content", post.content);
                    sub_dict->SetValue("user_id", post.user_id);
                    sub_dict->SetValue("create_time", post.create_time);
                    sub_dict->SetValue("update_time", post.update_time);
                }

                const string path = "./template_html/forum.html";
                ctemplate::Template *tpl = ctemplate::Template::GetTemplate(path, ctemplate::DO_NOT_STRIP);
                tpl->Expand(html, &dict);
            }
            else
            {
                *html = "获取论坛帖子失败";
            }
        }

        void PostDetailPage(int post_id, string *html)
        {
            ForumPost post;
            vector<ForumComment> comments;

            if (_model.GetForumPost(post_id, &post) && _model.GetForumComments(post_id, &comments))
            {
                ctemplate::TemplateDictionary dict("post_detail");
                dict.SetIntValue("id", post.id);
                dict.SetIntValue("question_number", post.question_number);
                dict.SetValue("title", post.title);
                dict.SetValue("content", post.content);
                dict.SetValue("user_id", post.user_id);
                dict.SetValue("create_time", post.create_time);
                dict.SetValue("update_time", post.update_time);

                for (const auto &comment : comments)
                {
                    ctemplate::TemplateDictionary *sub_dict = dict.AddSectionDictionary("comments");
                    sub_dict->SetValue("content", comment.content);
                    sub_dict->SetValue("user_id", comment.user_id);
                    sub_dict->SetValue("create_time", comment.create_time);
                }

                const string path = "./template_html/post_detail.html";
                ctemplate::Template *tpl = ctemplate::Template::GetTemplate(path, ctemplate::DO_NOT_STRIP);
                tpl->Expand(html, &dict);
            }
            else
            {
                *html = "获取帖子详情失败";
            }
        }

        void AddPost(const Request &req, Response &res)
        {
            // 验证用户是否登录
            string user_id;
            if (!GetUserIdFromCookie(req, user_id))
            {
                res.status = 401;
                res.set_content("请先登录", "text/plain");
                return;
            }

            LOG(INFO) << "用户 " << user_id << " 尝试发帖" << endl;

            // 解析请求体
            Json::Value post_info;
            if (!Json::Reader().parse(req.body, post_info))
            {
                res.status = 400;
                res.set_content("请求格式错误", "text/plain");
                return;
            }

            // 验证必要字段
            if (!post_info.isMember("question_number") || !post_info.isMember("title") || !post_info.isMember("content"))
            {
                res.status = 400;
                res.set_content("缺少必要字段", "text/plain");
                return;
            }

            ForumPost post;
            post.question_number = post_info["question_number"].asInt();
            post.user_id = user_id; // 使用cookie中的用户名
            post.title = post_info["title"].asString();
            post.content = post_info["content"].asString();

            LOG(INFO) << "准备添加帖子: 题目编号=" << post.question_number
                      << ", 用户=" << post.user_id
                      << ", 标题=" << post.title << endl;

            if (_model.AddForumPost(post))
            {
                LOG(INFO) << "发帖成功" << endl;
                res.set_content("发帖成功", "text/plain");
            }
            else
            {
                LOG(ERROR) << "发帖失败" << endl;
                res.status = 500;
                res.set_content("发帖失败", "text/plain");
            }
        }

        void AddComment(const Request &req, Response &res)
        {
            // 验证用户是否登录
            string user_id;
            if (!GetUserIdFromCookie(req, user_id))
            {
                res.status = 401;
                res.set_content("请先登录", "text/plain");
                return;
            }

            // 解析请求体
            Json::Value comment_info;
            Json::Reader().parse(req.body, comment_info);

            ForumComment comment;
            comment.post_id = comment_info["post_id"].asInt();
            comment.user_id = user_id; // 使用cookie中的用户名
            comment.content = comment_info["content"].asString();

            if (_model.AddForumComment(comment))
            {
                res.set_content("评论成功", "text/plain");
            }
            else
            {
                res.status = 500;
                res.set_content("评论失败", "text/plain");
            }
        }

    private:
        string UrlDecode(const string &str)
        {
            string result;
            for (size_t i = 0; i < str.length(); ++i)
            {
                if (str[i] == '%' && i + 2 < str.length())
                {
                    int value;
                    if (sscanf(str.substr(i + 1, 2).c_str(), "%x", &value) == 1)
                    {
                        result += static_cast<char>(value);
                        i += 2;
                        continue;
                    }
                }
                else if (str[i] == '+')
                {
                    result += ' ';
                    continue;
                }
                result += str[i];
            }
            return result;
        }

        bool GetUserIdFromCookie(const Request &req, string &user_id)
        {
            if (req.has_header("Cookie"))
            {
                string cookies = req.get_header_value("Cookie");

                size_t pos = cookies.find("user_id=");
                if (pos != string::npos)
                {
                    pos += 8; // "user_id=" 的长度
                    size_t end = cookies.find(';', pos);
                    user_id = (end != string::npos) ? cookies.substr(pos, end - pos) : cookies.substr(pos);
                    user_id = UrlDecode(user_id); // URL解码
                    return !user_id.empty();
                }
                else
                {
                    LOG(INFO) << "Cookie中未找到user_id" << endl;
                }
            }
            else
            {
                LOG(INFO) << "请求中没有Cookie头" << endl;
            }
            return false;
        }

        bool GetAdminFromCookie(const Request &req, string &admin)
        {
            auto cookie = req.get_header_value("Cookie");
            if (cookie.empty())
                return false;

            size_t pos = cookie.find("admin=");
            if (pos == string::npos)
                return false;

            size_t start = pos + 6;
            size_t end = cookie.find(";", start);
            if (end == string::npos)
                admin = cookie.substr(start);
            else
                admin = cookie.substr(start, end - start);

            return !admin.empty();
        }

    public:
        void RenderSubmissions(const vector<UserModel::Submission> &subs, string *html)
        {
            const string path = "./template_html/user_submissions.html";
            ctemplate::TemplateDictionary dict("submissions");

            for (const auto &sub : subs)
            {
                ctemplate::TemplateDictionary *item = dict.AddSectionDictionary("submission_list");
                item->SetValue("number", sub.number);
                item->SetValue("title", sub.title);
                item->SetValue("star", sub.star);
                item->SetValue("status", sub.status);
                item->SetValue("submit_time", sub.submit_time);
                // item->SetValue("status_text", sub.is_passed ? "通过" : "未通过");
                // item->SetValue("status_class", sub.is_passed ? "status-passed" : "status-failed");
            }

            ctemplate::Template *tpl = ctemplate::Template::GetTemplate(path, ctemplate::DO_NOT_STRIP);
            tpl->Expand(html, &dict);
        }
    };
}